﻿#pragma once

#include "df_net.h"
#include "gzip/df_zip.h"


namespace df
{
	class Connecter
	{

	protected:
		//socket链接
		Net net_;
		//对方ip
		uint32_t remoteIp_ = 0;

		//数据包缓冲
		uint8_t * bufContent_ = nullptr;
		uint8_t * bufCompress_ = nullptr;

		//断包长度
		uint16_t breakSize_ = 0;

	public:
		//缓冲大小
		static const uint16_t bufferSize_ = 16 * 1024;

		//包头大小
		static const uint16_t headerSize_ = 4;

		//包内容长度
		static const uint16_t contentSize_ = bufferSize_ - headerSize_;

		//包头信息:4字节
		//16位包内容长度,8位标志,8位操作码
		struct Header
		{
			//包内容大小(小端)
			uint16_t size = 0;
			//标志位:
			uint8_t sign = 0;
			//是否使用zlib压缩
			static const uint8_t isCompressed = Binary<uint8_t, 1>::value;
			////是否为最后一个包
			static const uint8_t isLast = 2;

			inline bool IsCompressed() const
			{
				return (sign & isCompressed) > 0;
			}

			inline bool IsLast() const
			{
				return (sign & isLast) > 0;
			}


			//操作码:
			uint8_t op = 0;
			enum {
				op_null,
				op_ping,
				op_respond,

				_op_end,
			};

			//包内容指针
			uint8_t * buf = nullptr;
		};

		Connecter()
		{
			bufContent_ = new uint8_t[bufferSize_];
			bufCompress_ = new uint8_t[bufferSize_];
		}

		virtual ~Connecter()
		{
			delete[] bufCompress_;
			delete[] bufContent_;
		}

		virtual void OnConnect()
		{
			//Cout << net_.GetSocket() << "新的链接" << IpToStr(remoteIp_) << std::endl;
		}

		virtual void OnClosed()
		{
			//Cout << net_.GetSocket() << "链接断开" << IpToStr(remoteIp_) << std::endl;
		}


		//成功收到一条解析完成的数据包
		virtual void OnRecv(Header &)
		{
		}

		/** \brief 只能发送短数据,自动启用数据压缩
		 *
		 * \param buf const void* 数据
		 * \param bufSize int 长度必须小于bufSize
		 * \param sign uint8_t 标志位
		 * \param op uint8_t 操作码
		 * \return void Throw
		 *
		 */
		void SendOne(const void * buf, int bufSize, uint8_t sign, uint8_t op)
		{
			if (bufSize < 0 || bufSize>contentSize_)
			{
				DF_BREAK_POINT_MSG("参数错误");
				Throw_df("参数错误:bufSize" << bufSize);
			}
			uint32_t resLen = contentSize_;
			int res = df::Compress2zlib(buf, bufSize, bufCompress_ + headerSize_, &resLen);

			if (res == UnCompress::success && resLen < (uint32_t)bufSize)//压缩后长度减小
			{
				//启用压缩
				writeHeader((uint16_t)resLen, sign | Header::isCompressed, op, bufCompress_);
				resLen += headerSize_;
			}
			else
			{
				//不使用压缩
				writeHeader((uint16_t)bufSize, sign, op, bufCompress_);
				memcpy(bufCompress_ + headerSize_, buf, bufSize);
				resLen = bufSize + headerSize_;
			}

			if(net_.Send(bufCompress_, resLen) != (int)(resLen))
				ThrowLastErr(IpToStr() << "发送失败");
		}


		/** \brief 可发送长数据,并自动启用数据压缩
		 *
		 * \param buf const void* 数据
		 * \param bufSize int 长度超过contentSize_则分包发送
		 * \param op uint8_t 操作码
		 * \return void Throw
		 *
		 */
		void Send(const void * buf, int bufSize, uint8_t op, bool hasLast=true)
		{
			if (bufSize < 0)
			{
				DF_BREAK_POINT_MSG("参数错误");
				Throw_df("参数错误:bufSize" << bufSize);
			}
			uint8_t sign = 0;
			if (bufSize <= 0)
			{
				writeHeader(0, sign, op, bufCompress_);
				if (net_.Send(bufCompress_, headerSize_) != (int)(headerSize_))
					ThrowLastErr(IpToStr() << "发送失败");
			}

			//分包传输
			int compSize = 0;//已传输长度
			while (compSize < bufSize)
			{
				int curSize = contentSize_;
				if (bufSize - compSize <= contentSize_)//最后一包
				{
					curSize = bufSize - compSize;
					if (hasLast)
						sign |= Header::isLast;
				}

				SendOne((uint8_t *)buf + compSize, curSize, sign, op);

				compSize += curSize;
			}

		}


		/** \brief 写入包头
		 *
		 * \param contSize uint16_t
		 * \param sign uint8_t
		 * \param op uint8_t
		 * \param buf void*
		 * \return void
		 *
		 */
		inline void writeHeader(uint16_t contSize, uint8_t sign, uint8_t op, void * toBuf)
		{
			*(uint16_t*)toBuf = contSize;
			((uint8_t*)toBuf)[2] = sign;
			((uint8_t*)toBuf)[3] = op;
		}



		bool IsClosed()
		{

			return net_.GetSocket() == 0;
		}

		Net GetNet()
		{
			return net_;
		}

		static void IpToStr(uint32_t ip, String &str)
		{
			uint8_t * ips = (uint8_t *)&ip;
			str << ips[0] << tt_('.') << ips[1] << tt_('.') << ips[2] << tt_('.') << ips[3];
		}

		static String IpToStr(uint32_t ip)
		{
			String str;
			IpToStr(ip, str);
			return str;
		}

		String IpToStr()
		{
			String str;
			IpToStr(remoteIp_, str);
			return str;
		}



		/** \brief 阻塞接受数据
		 *
		 * \return virtual bool true链接正常,false连接失效
		 *
		 */
		virtual bool Recv()
		{
			int recvLen = net_.Recv(bufContent_ + breakSize_, bufferSize_ - breakSize_);
			if (recvLen <= 0)
			{
				return false;
			}

			return OnRecvRaw(bufContent_, recvLen + breakSize_);
		}


		/** \brief 使用指定链接类型链接主机
		 *
		 * \param host const df::CCa& 主机
		 * \param port uint16_t 端口
		 * \param conn TConnecter& 返回链接对象
		 * \return void
		 *
		 */
		template<class TConnecter>
		static void Connect(const df::CCa & host, uint16_t port, TConnecter & conn)
		{
			int32_t ipOut;
			auto net = Net::Connect(host, port, ipOut);
			conn.net_ = net;
			conn.remoteIp_ = ipOut;
		}

		/** \brief 使用指定链接类型监听端口
		 *
		 * \param port uint16_t 端口
		 * \param maxConnecter int* 最大链接数
		 * \param 0 int recvTimeOut= recv超时值
		 * \return void
		 *
		 */
		template<class TConnecter>
		static void Listen(uint16_t port, int * maxConnecter, int recvTimeOut = 0)
		{
			if (port == 0)
				Throw_df(tt_("端口号不能为0"));

			Net net = Net::BindListen(port);
			for (;;)
			{
				Net client;
				uint32_t ip;

				if (!net.Accept(client, ip))
				{
					DF_ERR(tcc_("net accept failed!"));
					continue;
				}


				if (*maxConnecter <= 0){
					client.Close();
					continue;
				}
				df::AtomDec(maxConnecter);

				if (recvTimeOut > 0)
				{
					client.SetRecvTimeOut(recvTimeOut);
				}


				try{

					df::Thread::Start([=]()mutable{

						ON_SCOPE_EXIT({
							client.Shutdown();
							df::AtomInc(maxConnecter);
						});

						TConnecter conn;
						conn.net_ = client;
						conn.remoteIp_ = ip;

						ON_SCOPE_EXIT({
							conn.OnClosed();
						});


						conn.OnConnect();

						for (;;)
						{
							if (!conn.Recv())
								break;
						}


					});

				}DF_CATCH_ALL_EXCEPTION({
					client.Shutdown();
				});
			}
		}


	protected:
		/** \brief 解析原始数据,处理断包,粘包
		*
		* \param buf uint8_t* 缓冲区首部
		* \param size uint32_t 传输长度(包含断包长度)
		* \return virtual bool 链接是否合法
		*
		*/
		virtual bool OnRecvRaw(uint8_t * bufStart, uint32_t size)
		{
			//长度不对,非法数据
			if (size > bufferSize_ || breakSize_ > bufferSize_)
			{
				DF_ERR(tcc_("size过长,非法链接:") << IpToStr() << tcc_("size:") << size);
				return false;
			}

			uint8_t * buf = bufStart;
			//循环处理粘包
			while (size > 0)
			{

				//接收长度不足headerSize,头信息断包
				if (size < headerSize_)
				{
					///将头信息断包部分移动到缓冲区首
					if (buf > bufStart)
						memmove(bufStart, buf, size);
					breakSize_ = (uint16_t)size;
					return true;
				}

				//解析头信息

				//2字节内容长度
				uint16_t contSize = *(uint16_t*)buf;

				////数据包过大,非法连接
				if (contSize > contentSize_)
				{
					DF_ERR(tcc_("contSize过长非法链接:") << IpToStr() << tcc_("contSize:") << size);
					return false;
				}

				//实际包长大于传输字节,断包
				if (contSize + headerSize_ > (uint16_t)size)
				{
					///将断包部分移动到缓冲区首
					if (buf > bufStart)
						memmove(bufStart, buf, size);
					breakSize_ = (uint16_t)size;
					return true;
				}


				//2字节附加包头信息
				buf += 2;
				Header he;
				he.sign = buf[0];
				he.op = buf[1];
				if (he.op >= Header::_op_end)
				{
					DF_ERR(tcc_("错误op:") << IpToStr() << tcc_(" op:") << he.op << tcc_(" sign:") << he.sign);
					return false;
				}

				//包内容
				buf += 2;
				//需要解压
				if (contSize > 0 && he.IsCompressed())
				{
					uint32_t len = contentSize_;
					int res = df::Uncompress(buf, contSize, bufCompress_, &len);
					if (len > contentSize_ || res != UnCompress::success)
					{
						DF_ERR(tcc_("解压失败:") << IpToStr() << tcc_("len:") << len << tcc_("res:") << res);
						return false;
					}
					he.buf = bufCompress_;
					he.size = (uint16_t)len;
				}
				else
				{
					he.buf = buf;
					he.size = contSize;
				}

				//解析完一个包
				breakSize_ = 0;
				OnRecv(he);

				//转至下一包
				buf += contSize;
				size -= (contSize + headerSize_);
			}

			return true;
		}

		DF_DISABLE_COPY_ASSIGN(Connecter);
	};
}
